/*
	Alfred.cpp
*/

#include "Alfred.h"


void Alfred::move()
{

	switch(diff)
	{
		case EASY:
			cout<<"EASY"<<endl;
			game->move(smart_move(0));
			break;

		case MEDIUM:
			cout<<"MEDIUM"<<endl;
			game->move(smart_move(1));
			break;

		case HARD:
			cout<<"HARD"<<endl;
			game->move(smart_move(3));
			break;

		default:
			cerr << "Difficulties not implemented";
			break;
	}
}

void Alfred::toggle_piece()
{
	if(piece == BLACK)
		piece = WHITE;
	else
		piece = BLACK;
}

void Alfred::set_difficulty(Difficulty dif)
{
	diff = dif;
}

const Move Alfred::random_move()
{
	int i = rand() % 15 + 1;
	int j = rand() % 15 + 1;

	while(!game->validate_move(i, j))
	{
		i = rand() % 15 + 1;
		j = rand() % 15 + 1;
	}

	Move m(i, j, piece);

	return m;
}

Move Alfred::random_move(Game game)
{
	int i = rand() % 15 + 1;
	int j = rand() % 15 + 1;

	while(!game.validate_move(i, j))
	{
		i = rand() % 15 + 1;
		j = rand() % 15 + 1;
	}

	Move m(i, j, piece);

	return m;
}

const Move Alfred::smart_move(const int depth)
{
	Game* g = new Game();
    *g = *game;
    Node n(g, false);

    int high_score = -99999;

    vector<Move> c_moves = g->possible_moves();

    if(c_moves.size() == 0)
    	return random_move();

    vector<Move> best_moves;

    for(size_t i = 0; i < c_moves.size(); ++i)
    {
        c_moves[i].piece = piece;
        g->move(c_moves[i]);
        
        int tmp_score = Minimax(n, -99999, 99999, depth, swap(piece));
        
		n.game->undo1();
        
        if(tmp_score == 99999)
            return c_moves[i];
        else if (tmp_score > high_score) 
        {
            best_moves.clear();
            best_moves.push_back(c_moves[i]);
            high_score = tmp_score;
		}
        else if(tmp_score == high_score)
            best_moves.push_back(c_moves[i]);
    }

    int pos = rand()%(best_moves.size());
    return best_moves.at(pos);
}

char Alfred::swap(const char c)
{
	if(c == BLACK)
		return WHITE;
	else return BLACK;
}

int Alfred::runs_check(const Board board, int r, int c, int xs, int ys, char p)
{
	int count = 0;
	for(int i = 1; i < 5; i++)
		if(board.two_d[r+xs*i][c+ys*i] == p)
			count+=1;
        else break;

	return count;
}

int Alfred::runs_score(const Board board, const char p)
{
	int run2, run3, run4 = 0;

	for (int r = 0; r < 15; ++r)
	{
		for (int c = 0; c < 15; ++c)
		{
			char pos = board.two_d[r][c];

			if(pos != BLANK && pos == p)
			{
				if(r < 11)
				{ 
					int count = runs_check(board ,r, c, 1, 0, p);
					switch(count)
					{
						case 0: case 1: break;
						case 2: run2+=1; break;
						case 3: run3+=1; break;
						case 4:	run4+=1; break;
						case 5:	return 999999; break;
					}
				}
				if(c < 11)
				{
					int c1 = runs_check(board ,r, c, 0, 1, p);
					switch(c1)
					{
						case 0: case 1: break;
						case 2: run2+=1; break;
						case 3: run3+=1; break;
						case 4:	run4+=1; break;
						case 5:	return 999999; break;
					}
					if(r < 11)
					{
						int c2 = runs_check(board ,r, c, 1, 1, p);
						switch(c2)
						{
							case 0: case 1: break;
							case 2: run2+=1; break;
							case 3: run3+=1; break;
							case 4:	run4+=1; break;
							case 5:	return 999999; break;
						}
					}
				}

				if(c >= 4 && r < 11)
				{
					int count = runs_check(board ,r, c, 1, -1, p);
					switch(count)
					{
						case 0: case 1: break;
						case 2: run2+=1; break;
						case 3: run3+=1; break;
						case 4:	run4+=1; break;
						case 5:	return 99999; break;
					}
				}
			}
		}
	}

	return run2 + (run3*16) + (run4*32);
}

int Alfred::evaluate(Board board, char piece)
{
	int offense = runs_score(board, piece);
	int defense = runs_score(board, swap(piece));

	return offense - defense;
}

int Alfred::Minimax(Node node, int alpha, int beta, int depth, char player)
{
	int open_spaces = node.game->moves_left();

	if((open_spaces == 0) || (depth == 0))
    {
        int value = evaluate(node.game->board, player);
        return value;
    }
    
	vector<Move> child_moves = node.game->possible_moves();

// beta cut
	if(node.max)
	{
		for(size_t i = 0; i < child_moves.size(); ++i)
		{
            child_moves[i].piece = player;
            
            node.game->move(child_moves[i]);

            node.max = !node.max;

            alpha = max(alpha, Minimax(node, alpha, beta, depth-1, swap(player)));
           
	        node.game->undo1();

            if(beta <= alpha) break;
        }
            
		return alpha;
	}
// alpha cut
	else
	{   
		for(size_t i = 0; i < child_moves.size(); ++i)
		{
            child_moves[i].piece = player;
            
            node.game->move(child_moves[i]);

			node.max = !node.max;

            beta = min(beta, Minimax(node, alpha, beta, depth-1, swap(player)));
            
	        node.game->undo1();
            
            if(beta <= alpha) break;
		}
        
		return beta;
    }
}
